/**
 * Copyright (c) 2009 blurredVision
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
 * associated documentation files (the "Software"), to deal in the Software without restriction, 
 * including without limitation the rights to use, copy, modify, merge, publish, distribute, 
 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all copies or 
 * substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 
 * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 
 * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
#include "StdAfx.h"
#include "SoundBuffer_OpenAL.h"
#include <vector>
#include <vorbis/vorbisfile.h>
#include "vorbis/codec.h"

namespace openAL{

#define OGG_BUFFER_SIZE     32768       // 32 KB buffers

SoundBuffer_OpenAL::SoundBuffer_OpenAL(const std::string &sFileName) :	
	m_uiBufferID(0)
{
	alGetError();
	alGenBuffers(1, &m_uiBufferID);
	
	if (alGetError() != AL_NO_ERROR)
	{
		std::stringstream str;
		str << "Error generating a buffer" << std::endl;
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
	}

	if ( !LoadOggToBuffer(sFileName, m_uiBufferID))
	{
		std::stringstream str;
		str << "Error filling the buffer with ogg data" << std::endl;
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
	}
}

SoundBuffer_OpenAL::~SoundBuffer_OpenAL(void)
{
	alGetError();
	alDeleteBuffers( 1, &m_uiBufferID);
	if (alGetError() != AL_NO_ERROR)
	{
		std::stringstream str;
		str << "Error deleting buffer" << std::endl;
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
	}

}

ALuint SoundBuffer_OpenAL::getBufferID()
{
	return m_uiBufferID;
}

bool SoundBuffer_OpenAL::LoadOggToBuffer(const std::string &sFileName, ALuint uiBufferID)
{
	int endian = 0;                         // 0 for Little-Endian ( x86)
    int bitStream;
    long bytes;
    char array[OGG_BUFFER_SIZE];            // Local fixed size array
    FILE *f = NULL;							// The file pointer to the oggfile
	ALenum format;                          // The sound data format
	std::vector<char> buffer;				// The sound buffer data from file
	ALsizei freq;							// The frequency of the sound data
	vorbis_info *pInfo;						// a pointer to some informations about the ogg file
    OggVorbis_File oggFile;					// the temporary oggfile

    // Open for binary reading
	fopen_s(&f, sFileName.c_str(), "rb");

    if (f == NULL){
		std::stringstream str;
		str << "Cannot open " << sFileName << " for reading..." << std::endl;
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
		return false;
    }// end if

	// open the file/pipe on stdin
	if(ov_open_callbacks(f,&oggFile,NULL,-1,OV_CALLBACKS_DEFAULT)==-1){
		std::stringstream str;
		str << "Could not open input as an OggVorbis file." << std::endl;
		coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
		return false;
	}

    // Get some information about the OGG file ( format, frequency...)
    pInfo = ov_info(&oggFile, -1);

    // Check the number of channels... always use 16-bit samples
    if (pInfo->channels == 1)
        format = AL_FORMAT_MONO16;
    else
        format = AL_FORMAT_STEREO16;
    // end if

    freq = pInfo->rate; // The frequency of the sampling rate

	do{ // Keep reading until all is read

		// Read up to a buffer's worth of decoded sound data
		bytes = ov_read(&oggFile, array, OGG_BUFFER_SIZE, endian, 2, 1, &bitStream);

		if (bytes < 0){
			ov_clear(&oggFile);

			std::stringstream str;
			str << "Error decoding " << sFileName << "..." << std::endl;
			coreGetLog()->WriteError(str.str(),__FILE__,__LINE__);
			return false;
		} // end if

		// Append to end of buffer
		buffer.insert(buffer.end(), array, array + bytes);
    } while (bytes > 0);

	alGetError();
	// copy the buffer content into an openAL-Buffer
	alBufferData(uiBufferID, format, &buffer[0], static_cast<ALsizei>(buffer.size()), freq);

    ov_clear(&oggFile); // Clean up!
	//fclose(f); Wird bei OV_CALLBACKS_DEFAULT nicht benoetigt. Nur bei OV_CALLBACKS_NOCLOSE...

	if (alGetError() == AL_NO_ERROR)
		return true;
	else
		return false;
}

} // namespace

